using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace PokerLearner.Poker
{
	

	public class PokerLogger
	{
		#region Singleton Instance

		private static PokerLogger instance;
		public static PokerLogger Instance
		{
			get 
			{
				if (instance == null)
					instance = new PokerLogger();
				return instance; 
			}
		}

		#endregion

		#region Data Filename

		public static string DataFilename = "records.bin";

		#endregion

		#region Records

		private Dictionary<Player, Record> openRecords;

		public void OpenRecords(List<Player> players)
		{
			// Open record in poker logger
			int index = 0;
			foreach (Player player in players)
			{
				Position position = Position.Normal;
				if (index == 0)
					position = Position.SmallBlind;
				else if (index == 1)
					position = Position.BigBlind;

				// Make dealer more important 
				if (index == players.Count - 1)
					position = Position.Dealer;

				OpenRecord(player, position, players.Count);
				index++;
			}
		}

		public void OpenRecord(Player player, Position position, int numPlayers)
		{
			Record rec = new Record();
			rec.Position = position;
			rec.NumPlayers = numPlayers;
			openRecords.Add(player, rec);
		}

		public void SetNumPlayers(int numPlayers)
		{
			foreach (Record record in openRecords.Values)
			{
				if (record.RiverHand != null)
					record.NumPlayersRiver = numPlayers;
				else if (record.TurnHand != null)
					record.NumPlayersTurn = numPlayers;
				else if (record.FlopHand != null)
					record.NumPlayersFlop = numPlayers;
			}
		}

		/// <summary>
		/// Add a new play to the player record. Blind plays are ignored for now.
		/// </summary>
		/// <param name="player"></param>
		/// <param name="play"></param>
		public void AddPlay(Player player, Play play)
		{
			Record record = openRecords[player];
			if (record.RiverHand != null)
			{
				if (record.RiverPlays == null)
					record.RiverPlays = new List<Play>();
				record.RiverPlays.Add(play);
			}
			else if (record.TurnHand != null)
			{
				if (record.TurnPlays == null)
					record.TurnPlays = new List<Play>();
				record.TurnPlays.Add(play);
			}
			else if (record.FlopHand != null)
			{
				if (record.FlopPlays == null)
					record.FlopPlays = new List<Play>();
				record.FlopPlays.Add(play);
			}
			else if (record.PreFlopHand != null)
			{
				if (record.PreFlopPlays == null)
					record.PreFlopPlays = new List<Play>();
				record.PreFlopPlays.Add(play);
			}
		}


		public void AddHand(Player player, Hand hand)
		{
			Record record = openRecords[player];
			if (record.PreFlopHand != null)
			{
				if (record.FlopHand != null)
				{
					if (record.TurnHand != null)
					{
						System.Diagnostics.Debug.Assert(record.RiverHand == null);
						record.RiverHand = hand;
					}
					else
						record.TurnHand = hand;
				}
				else
					record.FlopHand = hand;
			}
			else
				record.PreFlopHand = hand;
		}

		public void AddCommonHand(Player player, Hand hand)
		{
			Record record = openRecords[player];
			if (record.FlopCommonHand != null)
			{
				if (record.TurnCommonHand != null)
				{
					System.Diagnostics.Debug.Assert(record.RiverCommonHand == null);
					record.RiverCommonHand = hand;
				}
				else
					record.TurnCommonHand = hand;
			}
			else
				record.FlopCommonHand = hand;
		}

		public void CloseRecord(Player player, int outcome)
		{
			Record record = openRecords[player];
			record.Value = outcome;

			record.Identifier = permanentRecords.Count;
			permanentRecords.Add(record);

			if (RecordAdded != null)
				RecordAdded(this, new RecordEventArgs(record));

			openRecords.Remove(player);
		}

		private List<Record> permanentRecords;
		public ReadOnlyCollection<Record> PermanentRecords
		{
			get { return permanentRecords.AsReadOnly(); }
		}

		#endregion

		#region Permanent Storage

		public void SaveRecords()
		{
			FileStream output = new FileStream(PokerLogger.DataFilename, FileMode.OpenOrCreate);
			BinaryFormatter formatter = new BinaryFormatter();
			formatter.Serialize(output, permanentRecords);
			output.Close();
		}

		public void ReadRecords()
		{
			try
			{
				FileStream input = new FileStream(PokerLogger.DataFilename, FileMode.Open);
				BinaryFormatter formatter = new BinaryFormatter();
				permanentRecords = (List<Record>)formatter.Deserialize(input);
				input.Close();

				if (RecordsRead != null)
					RecordsRead(this, null);
			}
			catch (Exception e) 
			{
				System.Diagnostics.Debug.WriteLine(e.Message);
			}
		}

		#endregion

		#region Events

		public event EventHandler<RecordEventArgs> RecordAdded;
		public event EventHandler RecordsRead;

		#endregion

		#region Constructor

		protected PokerLogger()
		{
			openRecords = new Dictionary<Player, Record>();
			permanentRecords = new List<Record>();
		}

		#endregion

	}
}
